// event-bus.ts
// 定义一个事件总线类，用于跨组件/模块的事件通信
class EventBus {
  // 私有属性：存储事件与回调函数的映射关系
  // 键为事件名称（string类型），值为回调函数数组（每个函数可接收任意参数并返回任意值）
  // 使用Map数据结构，方便快速查找和修改事件对应的回调列表
  private events: Map<string, Array<(...args: any[]) => void>> = new Map();

  /**
   * 订阅事件：注册一个回调函数，当事件触发时执行
   * @param eventName 事件名称，用于标识不同的事件
   * @param callback 事件触发时要执行的回调函数
   */
  on(eventName: string, callback: (...args: any[]) => void): void {
    // 检查当前事件是否已存在于映射表中
    if (!this.events.has(eventName)) {
      // 如果不存在，初始化一个空数组用于存储该事件的回调函数
      this.events.set(eventName, []);
    }
    // 从映射表中获取当前事件的回调数组，并将新的回调函数添加到数组中
    // 使用非空断言（!）是因为前面已确保该事件存在对应的数组
    this.events.get(eventName)!.push(callback);
  }

  /**
   * 发布事件：触发指定事件的所有订阅回调函数
   * @param eventName 要触发的事件名称
   * @param args 传递给回调函数的参数（可变参数，支持任意数量和类型）
   */
  emit(eventName: string, ...args: any[]): void {
    // 从映射表中获取当前事件对应的回调数组
    const callbacks = this.events.get(eventName);
    // 如果存在回调数组（即有订阅者）
    if (callbacks) {
      // 复制一份回调数组再执行（使用扩展运算符[...]）
      // 防止在回调执行过程中对原数组进行修改（如删除回调）导致的遍历异常
      [...callbacks].forEach(callback => {
        // 调用每个回调函数，并传递发布时的参数
        callback(...args);
      });
    }
  }

  /**
   * 取消订阅事件：移除指定事件的回调函数
   * @param eventName 事件名称
   * @param callback 可选参数，要移除的具体回调函数；如果不传，则移除该事件的所有回调
   */
  off(eventName: string, callback?: (...args: any[]) => void): void {
    // 从映射表中获取当前事件对应的回调数组
    const callbacks = this.events.get(eventName);
    // 如果事件不存在对应的回调数组，直接返回
    if (!callbacks) return;

    // 如果传入了具体的回调函数
    if (callback) {
      // 过滤掉数组中与目标回调函数相同的元素，保留其他回调
      // 然后更新映射表中该事件的回调数组
      this.events.set(
        eventName,
        callbacks.filter(cb => cb !== callback)
      );
    } else {
      // 如果未传入回调函数，则直接从映射表中删除该事件的所有回调
      this.events.delete(eventName);
    }
  }

  /**
   * 订阅一次性事件：事件触发一次后自动取消订阅
   * @param eventName 事件名称
   * @param callback 事件触发时的回调函数
   */
  once(eventName: string, callback: (...args: any[]) => void): void {
    // 定义一个包装回调函数
    const wrapCallback = (...args: any[]) => {
      // 先执行原始回调函数，并传递事件参数
      callback(...args);
      // 执行完成后，立即取消当前包装函数的订阅
      // 确保该事件只会触发一次
      this.off(eventName, wrapCallback);
    };
    // 将包装函数注册为事件的订阅者
    this.on(eventName, wrapCallback);
  }
}

// 导出一个单例模式的EventBus实例
// 确保整个应用中使用的是同一个事件总线实例，保证事件通信的一致性
export const eventBus = new EventBus();